<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js"><span id='global-property-'>/**
</span> * @ignore
 * a scalable client io framework
 * @author yiminghe@gmail.com
 */
KISSY.add('io/base', function (S, CustomEvent, undefined) {
    var rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|widget)$/,
        rspace = /\s+/,
        logger = S.getLogger('s/io'),
        mirror = function (s) {
            return s;
        },
        Promise = S.Promise,
        rnoContent = /^(?:GET|HEAD)$/,
        win = S.Env.host,
        location = win.location || {},
<span id='global-property-'>        simulatedLocation = /**
</span>         @type KISSY.Uri
         @ignore*/new S.Uri(location.href),
        isLocal = simulatedLocation &amp;&amp; rlocalProtocol.test(simulatedLocation.getScheme()),
        transports = {},
        defaultConfig = {
            type: 'GET',
            contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
            async: true,
            serializeArray: true,
            processData: true,
            accepts: {
                xml: 'application/xml, text/xml',
                html: 'text/html',
                text: 'text/plain',
                json: 'application/json, text/javascript',
                '*': '*/*'
            },
            converters: {
                text: {
                    json: S.parseJson,
                    html: mirror,
                    text: mirror,
                    xml: S.parseXML
                }
            },
            headers: {
                'X-Requested-With': 'XMLHttpRequest'
            },
            contents: {
                xml: /xml/,
                html: /html/,
                json: /json/
            }
        };

    defaultConfig.converters.html = defaultConfig.converters.text;

    function setUpConfig(c) {

        // deep mix,exclude context!
        var context = c.context;
        delete c.context;
        c = S.mix(S.clone(defaultConfig), c, {
            deep: true
        });
        c.context = context || c;

        var data, uri,
            type = c.type,
            dataType = c.dataType;

        uri = c.uri = simulatedLocation.resolve(c.url);

        // see method _getUrlForSend
        c.uri.setQuery('');

        if (!('crossDomain' in c)) {
            c.crossDomain = !c.uri.isSameOriginAs(simulatedLocation);
        }

        type = c.type = type.toUpperCase();
        c.hasContent = !rnoContent.test(type);

        if (c.processData &amp;&amp; (data = c.data) &amp;&amp; typeof data != 'string') {
            // normalize to string
            c.data = S.param(data, undefined, undefined, c.serializeArray);
        }

        // 数据类型处理链，一步步将前面的数据类型转化成最后一个
        dataType = c.dataType = S.trim(dataType || '*').split(rspace);

        if (!('cache' in c) &amp;&amp; S.inArray(dataType[0], ['script', 'jsonp'])) {
            c.cache = false;
        }

        if (!c.hasContent) {
            if (c.data) {
                uri.query.add(S.unparam(c.data));
            }
            if (c.cache === false) {
                uri.query.set('_ksTS', (S.now() + '_' + S.guid()));
            }
        }
        return c;
    }

<span id='KISSY-IO-cfg-xdr'><span id='KISSY-IO-cfg-password'><span id='KISSY-IO-cfg-username'><span id='KISSY-IO-cfg-xhrFields'><span id='KISSY-IO-cfg-serializeArray'><span id='KISSY-IO-cfg-timeout'><span id='KISSY-IO-cfg-complete'><span id='KISSY-IO-cfg-error'><span id='KISSY-IO-cfg-success'><span id='KISSY-IO-cfg-beforeSend'><span id='KISSY-IO-cfg-scriptCharset'><span id='KISSY-IO-cfg-processData'><span id='KISSY-IO-cfg-ifModified'><span id='KISSY-IO-cfg-mimeType'><span id='KISSY-IO-cfg-jsonpCallback'><span id='KISSY-IO-cfg-jsonp'><span id='KISSY-IO-cfg-headers'><span id='KISSY-IO-cfg-dataType'><span id='KISSY-IO-cfg-data'><span id='KISSY-IO-cfg-crossDomain'><span id='KISSY-IO-cfg-converters'><span id='KISSY-IO-cfg-context'><span id='KISSY-IO-cfg-contents'><span id='KISSY-IO-cfg-cache'><span id='KISSY-IO-cfg-async'><span id='KISSY-IO-cfg-accepts'><span id='KISSY-IO-cfg-contentType'><span id='KISSY-IO-cfg-type'><span id='KISSY-IO-cfg-url'><span id='KISSY-IO'>    /**
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>     * Return a io object and send request by config.
     *
     * @class KISSY.IO
     * @extends KISSY.Promise
     *
     * @cfg {String} url
     * request destination
     *
     * @cfg {String} type request type.
     * eg: 'get','post'
     * Default to: 'get'
     *
     * @cfg {String} contentType
     * Default to: 'application/x-www-form-urlencoded; charset=UTF-8'
     * Data will always be transmitted to the server using UTF-8 charset
     *
     * @cfg {Object} accepts
     * Default to: depends on DataType.
     * The content type sent in request header that tells the server
     * what kind of response it will accept in return.
     * It is recommended to do so once in the {@link KISSY.IO#method-setupConfig}
     *
     * @cfg {Boolean} async
     * Default to: true
     * whether request is sent asynchronously
     *
     * @cfg {Boolean} cache
     * Default to: true ,false for dataType 'script' and 'jsonp'
     * if set false,will append _ksTs=Date.now() to url automatically
     *
     * @cfg {Object} contents
     * a name-regexp map to determine request data's dataType
     * It is recommended to do so once in the {@link KISSY.IO#method-setupConfig}
     *
     * @cfg {Object} context
     * specify the context of this request 's callback (success,error,complete)
     *
     * @cfg {Object} converters
     * Default to: {text:{json:Json.parse,html:mirror,text:mirror,xml:KISSY.parseXML}}
     * specified how to transform one dataType to another dataType
     * It is recommended to do so once in the {@link KISSY.IO#method-setupConfig}
     *
     * @cfg {Boolean} crossDomain
     * Default to: false for same-domain request,true for cross-domain request
     * if server-side jsonp redirect to another domain, you should set this to true.
     * if you want use script for jsonp for same domain request, you should set this to true.
     *
     * @cfg {Object} data
     * Data sent to server.if processData is true,data will be serialized to String type.
     * if value if an Array, serialization will be based on serializeArray.
     *
     * @cfg {String} dataType
     * return data as a specified type
     * Default to: Based on server contentType header
     * 'xml' : a XML document
     * 'text'/'html': raw server data
     * 'script': evaluate the return data as script
     * 'json': parse the return data as json and return the result as final data
     * 'jsonp': load json data via jsonp
     *
     * @cfg {Object} headers
     * additional name-value header to send along with this request.
     *
     * @cfg {String} jsonp
     * Default to: 'callback'
     * Override the callback function name in a jsonp request. eg:
     * set 'callback2' , then jsonp url will append  'callback2=?'.
     *
     * @cfg {String} jsonpCallback
     * Specify the callback function name for a jsonp request.
     * set this value will replace the auto generated function name.
     * eg:
     * set 'customCall' , then jsonp url will append 'callback=customCall'
     *
     * @cfg {String} mimeType
     * override xhr 's mime type
     *
     * @cfg {String} ifModified
     * whether enter if modified mode.
     * Defaults to false.
     *
     * @cfg {Boolean} processData
     * Default to: true
     * whether data will be serialized as String
     *
     * @cfg {String} scriptCharset
     * only for dataType 'jsonp' and 'script' and 'get' type.
     * force the script to certain charset.
     *
     * @cfg {Function} beforeSend
     * beforeSend(io,config)
     * callback function called before the request is sent.this function has 2 arguments
     *
     * 1. current KISSY io object
     *
     * 2. current io config
     *
     * note: can be used for add progress event listener for native xhr's upload attribute
     * see &lt;a href='http://www.w3.org/TR/XMLHttpRequest/#event-xhr-progress'&gt;XMLHttpRequest2&lt;/a&gt;
     *
     * @cfg {Function} success
     * success(data,textStatus,xhr)
     * callback function called if the request succeeds.this function has 3 arguments
     *
     * 1. data returned from this request with type specified by dataType
     *
     * 2. status of this request with type String
     *
     * 3. io object of this request , for details {@link KISSY.IO}
     *
     * @cfg {Function} error
     * success(data,textStatus,xhr)
     * callback function called if the request occurs error.this function has 3 arguments
     *
     * 1. null value
     *
     * 2. status of this request with type String,such as 'timeout','Not Found','parsererror:...'
     *
     * 3. io object of this request , for details {@link KISSY.IO}
     *
     * @cfg {Function} complete
     * success(data,textStatus,xhr)
     * callback function called if the request finished(success or error).this function has 3 arguments
     *
     * 1. null value if error occurs or data returned from server
     *
     * 2. status of this request with type String,such as success:'ok',
     * error:'timeout','Not Found','parsererror:...'
     *
     * 3. io object of this request , for details {@link KISSY.IO}
     *
     * @cfg {Number} timeout
     * Set a timeout(in seconds) for this request.if will call error when timeout
     *
     * @cfg {Boolean} serializeArray
     * whether add [] to data's name when data's value is array in serialization
     *
     * @cfg {Object} xhrFields
     * name-value to set to native xhr.set as xhrFields:{withCredentials:true}
     * note: withCredentials defaults to true.
     *
     * @cfg {String} username
     * a username tobe used in response to HTTP access authentication request
     *
     * @cfg {String} password
     * a password tobe used in response to HTTP access authentication request
     *
     * @cfg {Object} xdr
     * cross domain request config object, contains sub config:
     *
     * xdr.src
     * Default to: KISSY 's flash url
     * flash sender url
     *
     * xdr.use
     * if set to 'use', it will always use flash for cross domain request even in chrome/firefox
     *
     * xdr.subDomain
     * cross sub domain request config object
     *
     * xdr.subDomain.proxy
     * proxy page,eg:     *
     * a.t.cn/a.htm send request to b.t.cn/b.htm:
     *
     * 1. a.htm set &lt;code&gt; document.domain='t.cn' &lt;/code&gt;
     *
     * 2. b.t.cn/proxy.htm 's content is &lt;code&gt; &amp;lt;script&gt;document.domain='t.cn'&amp;lt;/script&gt; &lt;/code&gt;
     *
     * 3. in a.htm , call &lt;code&gt; IO({xdr:{subDomain:{proxy:'/proxy.htm'}}}) &lt;/code&gt;
     *
     */
    function IO(c) {

        var self = this;

        if (!(self instanceof IO)) {
            return new IO(c);
        }

        Promise.call(self);

        c = setUpConfig(c);

        S.mix(self, {
            // 结构化数据，如 json
            responseData: null,
<span id='KISSY-IO-property-config'>            /**
</span>             * config of current IO instance.
             * @member KISSY.IO
             * @property config
             * @type Object
             */
            config: c || {},
            timeoutTimer: null,

<span id='KISSY-IO-property-responseText'>            /**
</span>             * String typed data returned from server
             * @type String
             */
            responseText: null,
<span id='KISSY-IO-property-responseXML'>            /**
</span>             * xml typed data returned from server
             * @type String
             */
            responseXML: null,
            responseHeadersString: '',
            responseHeaders: null,
            requestHeaders: {},
<span id='KISSY-IO-property-readyState'>            /**
</span>             * readyState of current request
             * 0: initialized
             * 1: send
             * 4: completed
             * @type Number
             */
            readyState: 0,
            state: 0,
<span id='KISSY-IO-property-statusText'>            /**
</span>             * HTTP statusText of current request
             * @type String
             */
            statusText: null,
<span id='KISSY-IO-property-status'>            /**
</span>             * HTTP Status Code of current request
             * eg:
             * 200: ok
             * 404: Not Found
             * 500: Server Error
             * @type String
             */
            status: 0,
            transport: null
        });

        S.Defer(self);

        var transportConstructor,
            transport;

<span id='KISSY-IO-static-event-start'>        /**
</span>         * fired before generating request object
         * @event start
         * @member KISSY.IO
         * @static
         * @param {KISSY.Event.CustomEvent.Object} e
         * @param {KISSY.IO} e.io current io
         */
        IO.fire('start', {
            // 兼容
            ajaxConfig: c,
            io: self
        });

        transportConstructor = transports[c.dataType[0]] || transports['*'];
        transport = new transportConstructor(self);

        self.transport = transport;

        if (c.contentType) {
            self.setRequestHeader('Content-Type', c.contentType);
        }

        var dataType = c.dataType[0],
            i,
            timeout = c.timeout,
            context = c.context,
            headers = c.headers,
            accepts = c.accepts;

        // Set the Accepts header for the server, depending on the dataType
        self.setRequestHeader(
            'Accept',
            dataType &amp;&amp; accepts[dataType] ?
                accepts[ dataType ] + (dataType === '*' ? '' : ', */*; q=0.01'  ) :
                accepts[ '*' ]
        );

        // Check for headers option
        for (i in headers) {
            self.setRequestHeader(i, headers[ i ]);
        }


        // allow setup native listener
        // such as xhr.upload.addEventListener('progress', function (ev) {})
        if (c.beforeSend &amp;&amp; ( c.beforeSend.call(context, self, c) === false)) {
            return self;
        }

        self.readyState = 1;

<span id='KISSY-IO-static-event-send'>        /**
</span>         * fired before sending request
         * @event send
         * @member KISSY.IO
         * @static
         * @param {KISSY.Event.CustomEvent.Object} e
         * @param {KISSY.IO} e.io current io
         */

        IO.fire('send', {
            // 兼容
            ajaxConfig: c,
            io: self
        });

        // Timeout
        if (c.async &amp;&amp; timeout &gt; 0) {
            self.timeoutTimer = setTimeout(function () {
                self.abort('timeout');
            }, timeout * 1000);
        }

        try {
            // flag as sending
            self.state = 1;
            transport.send();
        } catch (e) {
            // Propagate exception as error if not done
            if (self.state &lt; 2) {
                logger.error(e.stack || e);
                self._ioReady(-1, e.message);
                // Simply rethrow otherwise
            } else {
                S.error(e);
            }
        }

        return self;
    }

    S.mix(IO, CustomEvent.targetObject);

    S.mix(IO, {
<span id='KISSY-IO-static-property-isLocal'>        /**
</span>         * whether current application is a local application
         * (protocal is file://,widget://,about://)
         * @type {Boolean}
         * @member KISSY.IO
         * @static
         */
        isLocal: isLocal,
<span id='KISSY-IO-static-method-setupConfig'>        /**
</span>         * name-value object that set default config value for io class
         * @param {Object} setting
         * @member KISSY.IO
         * @static
         */
        setupConfig: function (setting) {
            S.mix(defaultConfig, setting, {
                deep: true
            });
        },
<span id='KISSY-IO-static-method-setupTransport'>        /**
</span>         * @private
         * @member KISSY.IO
         * @static
         */
        'setupTransport': function (name, fn) {
            transports[name] = fn;
        },
<span id='KISSY-IO-static-method-getTransport'>        /**
</span>         * @private
         * @member KISSY.IO
         * @static
         */
        'getTransport': function (name) {
            return transports[name];
        },
<span id='KISSY-IO-static-method-getConfig'>        /**
</span>         * get default config value for io request
         * @return {Object}
         * @member KISSY.IO
         * @static
         */
        getConfig: function () {
            return defaultConfig;
        }
    });

    return IO;
}, {
    requires: ['event/custom']
});

/*

 // !TODO
 // 去除 event/custom 依赖，用户不载入就不能监听
 // 载入后通过 custom.on(IO,type) 监听


 2012-08-16
 - transform IO to class, remove XhrObject class.
 - support ifModified
 - http://bugs.jquery.com/ticket/8394
 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
 - https://github.com/kissyteam/kissy/issues/203

 2012-07-18 yiminghe@gmail.com
 - refactor by KISSY.Uri

 2012-2-07 yiminghe@gmail.com
 - 返回 Promise 类型对象，可以链式操作啦！

 2011 yiminghe@gmail.com
 - 借鉴 jquery，优化减少闭包使用

 */</pre>
</body>
</html>
